<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#修饰器"  class="anchor" id="user-content-修饰器">

    </a>修饰器
    </h1>
    <h2><a href="#类的修饰"  class="anchor" id="user-content-类的修饰">

    </a>类的修饰
    </h2>
    <p>许多面向对象的语言都有修饰器（Decorator）函数，用来修改类的行为。目前，有一个<a href="https://github.com/tc39/proposal-decorators">提案</a>将这项功能，引入了
        ECMAScript。</p>
    <div class="highlight highlight-source-js"><pre>@testable
<span class="pl-k">class</span> <span class="pl-en">MyTestableClass</span> {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-k">function</span> <span class="pl-en">testable</span>(<span class="pl-smi">target</span>) {
  <span class="pl-smi">target</span>.<span class="pl-smi">isTestable</span> <span class="pl-k">=</span> <span
                class="pl-c1">true</span>;
}

<span class="pl-smi">MyTestableClass</span>.<span class="pl-smi">isTestable</span> <span class="pl-c"><span
                class="pl-c">//</span> true</span></pre>
    </div>
    <p>
        上面代码中，<code>@testable</code>就是一个修饰器。它修改了<code>MyTestableClass</code>这个类的行为，为它加上了静态属性<code>isTestable</code>。<code>testable</code>函数的参数<code>target</code>是<code>MyTestableClass</code>类本身。
    </p>
    <p>基本上，修饰器的行为就是下面这样。</p>
    <div class="highlight highlight-source-js"><pre>@decorator
<span class="pl-k">class</span> <span class="pl-en">A</span> {}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>

<span class="pl-k">class</span> <span class="pl-en">A</span> {}
<span class="pl-c1">A</span> <span class="pl-k">=</span> <span class="pl-en">decorator</span>(<span
                class="pl-c1">A</span>) <span class="pl-k">||</span> <span class="pl-c1">A</span>;</pre>
    </div>
    <p>也就是说，修饰器是一个对类进行处理的函数。修饰器函数的第一个参数，就是所要修饰的目标类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">testable</span>(<span class="pl-smi">target</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}</pre>
    </div>
    <p>上面代码中，<code>testable</code>函数的参数<code>target</code>，就是会被修饰的类。</p>
    <p>如果觉得一个参数不够用，可以在修饰器外面再封装一层函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">testable</span>(<span class="pl-smi">isTestable</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>) {
    <span class="pl-smi">target</span>.<span class="pl-smi">isTestable</span> <span class="pl-k">=</span> isTestable;
  }
}

@<span class="pl-en">testable</span>(<span class="pl-c1">true</span>)
<span class="pl-k">class</span> <span class="pl-en">MyTestableClass</span> {}
<span class="pl-smi">MyTestableClass</span>.<span class="pl-smi">isTestable</span> <span class="pl-c"><span
                class="pl-c">//</span> true</span>

@<span class="pl-en">testable</span>(<span class="pl-c1">false</span>)
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}
<span class="pl-smi">MyClass</span>.<span class="pl-smi">isTestable</span> <span class="pl-c"><span
                class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码中，修饰器<code>testable</code>可以接受参数，这就等于可以修改修饰器的行为。</p>
    <p>注意，修饰器对类的行为的改变，是代码编译时发生的，而不是在运行时。这意味着，修饰器能在编译阶段运行代码。也就是说，修饰器本质就是编译时执行的函数。</p>
    <p>前面的例子是为类添加一个静态属性，如果想添加实例属性，可以通过目标类的<code>prototype</code>对象操作。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">testable</span>(<span class="pl-smi">target</span>) {
  <span class="pl-smi">target</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">isTestable</span> <span
                class="pl-k">=</span> <span class="pl-c1">true</span>;
}

@testable
<span class="pl-k">class</span> <span class="pl-en">MyTestableClass</span> {}

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">MyTestableClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-smi">isTestable</span> <span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，修饰器函数<code>testable</code>是在目标类的<code>prototype</code>对象上添加属性，因此就可以在实例上调用。</p>
    <p>下面是另外一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> mixins.js</span>
<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">mixins</span>(<span
                class="pl-k">...</span><span class="pl-v">list</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span> (<span class="pl-smi">target</span>) {
    <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-smi">target</span>.<span
                class="pl-c1">prototype</span>, <span class="pl-k">...</span>list)
  }
}

<span class="pl-c"><span class="pl-c">//</span> main.js</span>
<span class="pl-k">import</span> { <span class="pl-smi">mixins</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./mixins<span class="pl-pds">'</span></span>

<span class="pl-k">const</span> <span class="pl-c1">Foo</span> <span class="pl-k">=</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">mixins</span>(Foo)
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> 'foo'</span></pre>
    </div>
    <p>上面代码通过修饰器<code>mixins</code>，把<code>Foo</code>类的方法添加到了<code>MyClass</code>的实例上面。可以用<code>Object.assign()</code>模拟这个功能。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">Foo</span> <span
            class="pl-k">=</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}

<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-smi">MyClass</span>.<span
                class="pl-c1">prototype</span>, Foo);

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> 'foo'</span></pre>
    </div>
    <p>实际开发中，React 与 Redux 库结合使用时，常常需要写成下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyReactComponent</span> <span class="pl-k">extends</span> <span
            class="pl-e">React</span>.<span class="pl-smi">Component</span> {}

<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-smi">connect</span>(mapStateToProps, mapDispatchToProps)(MyReactComponent);</pre>
    </div>
    <p>有了装饰器，就可以改写上面的代码。</p>
    <div class="highlight highlight-source-js"><pre>@<span class="pl-en">connect</span>(mapStateToProps, mapDispatchToProps)
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">class</span> <span class="pl-en">MyReactComponent</span> <span
                class="pl-k">extends</span> <span class="pl-e">React</span>.<span
                class="pl-smi">Component</span> {}</pre>
    </div>
    <p>相对来说，后一种写法看上去更容易理解。</p>
    <h2><a href="#方法的修饰"  class="anchor" id="user-content-方法的修饰">

    </a>方法的修饰
    </h2>
    <p>修饰器不仅可以修饰类，还可以修饰类的属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @readonly
  <span class="pl-en">name</span>() { <span class="pl-k">return</span> <span class="pl-s"><span
                class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">this</span>.<span
                class="pl-smi">first</span><span class="pl-pse">}</span></span> <span class="pl-s1"><span
                class="pl-pse">${</span><span class="pl-c1">this</span>.<span class="pl-smi">last</span><span
                class="pl-pse">}</span></span><span class="pl-pds">`</span></span> }
}</pre>
    </div>
    <p>上面代码中，修饰器<code>readonly</code>用来修饰“类”的<code>name</code>方法。</p>
    <p>此时，修饰器函数一共可以接受三个参数，第一个参数是所要修饰的目标对象，即类的实例（这不同于类的修饰，那种情况时<code>target</code>参数指的是类本身）；第二个参数是所要修饰的属性名，第三个参数是该属性的描述对象。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">readonly</span>(<span class="pl-smi">target</span>, <span class="pl-smi">name</span>, <span
            class="pl-smi">descriptor</span>){
  <span class="pl-c"><span class="pl-c">//</span> descriptor对象原来的值如下</span>
  <span class="pl-c"><span class="pl-c">//</span> {</span>
  <span class="pl-c"><span class="pl-c">//</span>   value: specifiedFunction,</span>
  <span class="pl-c"><span class="pl-c">//</span>   enumerable: false,</span>
  <span class="pl-c"><span class="pl-c">//</span>   configurable: true,</span>
  <span class="pl-c"><span class="pl-c">//</span>   writable: true</span>
  <span class="pl-c"><span class="pl-c">//</span> };</span>
  <span class="pl-smi">descriptor</span>.<span class="pl-smi">writable</span> <span class="pl-k">=</span> <span
                class="pl-c1">false</span>;
  <span class="pl-k">return</span> descriptor;
}

<span class="pl-en">readonly</span>(<span class="pl-smi">Person</span>.<span class="pl-c1">prototype</span>, <span
                class="pl-s"><span class="pl-pds">'</span>name<span class="pl-pds">'</span></span>, descriptor);
<span class="pl-c"><span class="pl-c">//</span> 类似于</span>
<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(<span class="pl-smi">Person</span>.<span
                class="pl-c1">prototype</span>, <span class="pl-s"><span class="pl-pds">'</span>name<span
                class="pl-pds">'</span></span>, descriptor);</pre>
    </div>
    <p>上面代码说明，修饰器（readonly）会修改属性的描述对象（descriptor），然后被修改的描述对象再用来定义属性。</p>
    <p>下面是另一个例子，修改属性描述对象的<code>enumerable</code>属性，使得该属性不可遍历。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @nonenumerable
  <span class="pl-k">get</span> <span class="pl-en">kidCount</span>() { <span class="pl-k">return</span> <span
                class="pl-c1">this</span>.<span class="pl-smi">children</span>.<span class="pl-c1">length</span>; }
}

<span class="pl-k">function</span> <span class="pl-en">nonenumerable</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">name</span>, <span class="pl-smi">descriptor</span>) {
  <span class="pl-smi">descriptor</span>.<span class="pl-smi">enumerable</span> <span class="pl-k">=</span> <span
                class="pl-c1">false</span>;
  <span class="pl-k">return</span> descriptor;
}</pre>
    </div>
    <p>下面的<code>@log</code>修饰器，可以起到输出日志的作用。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Math</span> {
  @log
  <span class="pl-en">add</span>(<span class="pl-smi">a</span>, <span class="pl-smi">b</span>) {
    <span class="pl-k">return</span> a <span class="pl-k">+</span> b;
  }
}

<span class="pl-k">function</span> <span class="pl-en">log</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">name</span>, <span class="pl-smi">descriptor</span>) {
  <span class="pl-k">var</span> oldValue <span class="pl-k">=</span> <span class="pl-smi">descriptor</span>.<span
                class="pl-c1">value</span>;

  <span class="pl-smi">descriptor</span>.<span class="pl-en">value</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>Calling "<span
                class="pl-s1"><span class="pl-pse">${</span>name<span class="pl-pse">}</span></span>" with<span
                class="pl-pds">`</span></span>, <span class="pl-c1">arguments</span>);
    <span class="pl-k">return</span> <span class="pl-smi">oldValue</span>.<span class="pl-c1">apply</span>(<span
                class="pl-c1">null</span>, <span class="pl-c1">arguments</span>);
  };

  <span class="pl-k">return</span> descriptor;
}

<span class="pl-k">const</span> <span class="pl-c1">math</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">Math</span>();

<span class="pl-c"><span class="pl-c">//</span> passed parameters should get logged now</span>
<span class="pl-smi">math</span>.<span class="pl-c1">add</span>(<span class="pl-c1">2</span>, <span
                class="pl-c1">4</span>);</pre>
    </div>
    <p>上面代码中，<code>@log</code>修饰器的作用就是在执行原始的操作之前，执行一次<code>console.log</code>，从而达到输出日志的目的。</p>
    <p>修饰器有注释的作用。</p>
    <div class="highlight highlight-source-js"><pre>@testable
<span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @readonly
  @nonenumerable
  <span class="pl-en">name</span>() { <span class="pl-k">return</span> <span class="pl-s"><span
                class="pl-pds">`</span><span class="pl-s1"><span class="pl-pse">${</span><span class="pl-c1">this</span>.<span
                class="pl-smi">first</span><span class="pl-pse">}</span></span> <span class="pl-s1"><span
                class="pl-pse">${</span><span class="pl-c1">this</span>.<span class="pl-smi">last</span><span
                class="pl-pse">}</span></span><span class="pl-pds">`</span></span> }
}</pre>
    </div>
    <p>从上面代码中，我们一眼就能看出，<code>Person</code>类是可测试的，而<code>name</code>方法是只读和不可枚举的。</p>
    <p>下面是使用 Decorator 写法的<a href="https://github.com/ionic-team/stencil">组件</a>，看上去一目了然。</p>
    <div class="highlight highlight-source-js"><pre>@<span class="pl-en">Component</span>({
  tag<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>my-component<span
                class="pl-pds">'</span></span>,
  styleUrl<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>my-component.scss<span
                class="pl-pds">'</span></span>
})
<span class="pl-k">export</span> <span class="pl-k">class</span> <span class="pl-en">MyComponent</span> {
  @<span class="pl-en">Prop</span>() first<span class="pl-k">:</span> string;
  @<span class="pl-en">Prop</span>() last<span class="pl-k">:</span> string;
  @<span class="pl-en">State</span>() isVisible<span class="pl-k">:</span> boolean <span class="pl-k">=</span> <span
                class="pl-c1">true</span>;

  <span class="pl-en">render</span>() {
    <span class="pl-k">return</span> (
      <span class="pl-k">&lt;</span>p<span class="pl-k">&gt;</span>Hello, my name is {<span
                class="pl-c1">this</span>.<span class="pl-smi">first</span>} {<span class="pl-c1">this</span>.<span
                class="pl-smi">last</span>}<span class="pl-k">&lt;</span><span class="pl-k">/</span>p<span class="pl-k">&gt;</span>
    );
  }
}</pre>
    </div>
    <p>如果同一个方法有多个修饰器，会像剥洋葱一样，先从外到内进入，然后由内向外执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">dec</span>(<span class="pl-smi">id</span>){
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>evaluated<span
                class="pl-pds">'</span></span>, id);
  <span class="pl-k">return</span> (<span class="pl-smi">target</span>, <span class="pl-smi">property</span>, <span
                class="pl-smi">descriptor</span>) <span class="pl-k">=&gt;</span> <span
                class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>executed<span class="pl-pds">'</span></span>, id);
}

<span class="pl-k">class</span> <span class="pl-en">Example</span> {
    @<span class="pl-en">dec</span>(<span class="pl-c1">1</span>)
    @<span class="pl-en">dec</span>(<span class="pl-c1">2</span>)
    <span class="pl-en">method</span>(){}
}
<span class="pl-c"><span class="pl-c">//</span> evaluated 1</span>
<span class="pl-c"><span class="pl-c">//</span> evaluated 2</span>
<span class="pl-c"><span class="pl-c">//</span> executed 2</span>
<span class="pl-c"><span class="pl-c">//</span> executed 1</span></pre>
    </div>
    <p>上面代码中，外层修饰器<code>@dec(1)</code>先进入，但是内层修饰器<code>@dec(2)</code>先执行。</p>
    <p>除了注释，修饰器还能用来类型检查。所以，对于类来说，这项功能相当有用。从长期来看，它将是 JavaScript 代码静态分析的重要工具。</p>
    <h2><a href="#为什么修饰器不能用于函数"  class="anchor" id="user-content-为什么修饰器不能用于函数">

    </a>为什么修饰器不能用于函数？
    </h2>
    <p>修饰器只能用于类和类的方法，不能用于函数，因为存在函数提升。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> counter <span
            class="pl-k">=</span> <span class="pl-c1">0</span>;

<span class="pl-k">var</span> <span class="pl-en">add</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span> () {
  counter<span class="pl-k">++</span>;
};

@add
<span class="pl-k">function</span> <span class="pl-en">foo</span>() {
}</pre>
    </div>
    <p>上面的代码，意图是执行后<code>counter</code>等于 1，但是实际上结果是<code>counter</code>等于 0。因为函数提升，使得实际执行的代码是下面这样。</p>
    <div class="highlight highlight-source-js"><pre>@add
<span class="pl-k">function</span> <span class="pl-en">foo</span>() {
}

<span class="pl-k">var</span> counter;
<span class="pl-k">var</span> add;

counter <span class="pl-k">=</span> <span class="pl-c1">0</span>;

<span class="pl-en">add</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () {
  counter<span class="pl-k">++</span>;
};</pre>
    </div>
    <p>下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> readOnly <span
            class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">"</span>some-decorator<span
            class="pl-pds">"</span></span>);

@readOnly
<span class="pl-k">function</span> <span class="pl-en">foo</span>() {
}</pre>
    </div>
    <p>上面代码也有问题，因为实际执行是下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> readOnly;

@readOnly
<span class="pl-k">function</span> <span class="pl-en">foo</span>() {
}

readOnly <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">"</span>some-decorator<span
                class="pl-pds">"</span></span>);</pre>
    </div>
    <p>总之，由于存在函数提升，使得修饰器不能用于函数。类是不会提升的，所以就没有这方面的问题。</p>
    <p>另一方面，如果一定要修饰函数，可以采用高阶函数的形式直接执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">doSomething</span>(<span class="pl-smi">name</span>) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Hello, <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> name);
}

<span class="pl-k">function</span> <span class="pl-en">loggingDecorator</span>(<span class="pl-smi">wrapped</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Starting<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">const</span> <span class="pl-c1">result</span> <span class="pl-k">=</span> <span class="pl-smi">wrapped</span>.<span
                class="pl-c1">apply</span>(<span class="pl-c1">this</span>, <span class="pl-c1">arguments</span>);
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>Finished<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">return</span> result;
  }
}

<span class="pl-k">const</span> <span class="pl-c1">wrapped</span> <span class="pl-k">=</span> <span class="pl-en">loggingDecorator</span>(doSomething);</pre>
    </div>
    <h2><a href="#core-decoratorsjs"  class="anchor" id="user-content-core-decoratorsjs">

    </a>core-decorators.js
    </h2>
    <p><a href="https://github.com/jayphelps/core-decorators.js">core-decorators.js</a>是一个第三方模块，提供了几个常见的修饰器，通过它可以更好地理解修饰器。
    </p>
    <p><strong>（1）@autobind</strong></p>
    <p><code>autobind</code>修饰器使得方法中的<code>this</code>对象，绑定原始对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">autobind</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>core-decorators<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @autobind
  <span class="pl-en">getPerson</span>() {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>;
  }
}

<span class="pl-k">let</span> person <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Person</span>();
<span class="pl-k">let</span> getPerson <span class="pl-k">=</span> <span class="pl-smi">person</span>.<span
                class="pl-smi">getPerson</span>;

<span class="pl-en">getPerson</span>() <span class="pl-k">===</span> person;
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p><strong>（2）@readonly</strong></p>
    <p><code>readonly</code>修饰器使得属性或方法不可写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">readonly</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>core-decorators<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">Meal</span> {
  @readonly
  entree <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>steak<span class="pl-pds">'</span></span>;
}

<span class="pl-k">var</span> dinner <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Meal</span>();
<span class="pl-smi">dinner</span>.<span class="pl-smi">entree</span> <span class="pl-k">=</span> <span
                class="pl-s"><span class="pl-pds">'</span>salmon<span class="pl-pds">'</span></span>;
<span class="pl-c"><span
        class="pl-c">//</span> Cannot assign to read only property 'entree' of [object Object]</span></pre>
    </div>
    <p><strong>（3）@override</strong></p>
    <p><code>override</code>修饰器检查子类的方法，是否正确覆盖了父类的同名方法，如果不正确会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">override</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>core-decorators<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">Parent</span> {
  <span class="pl-en">speak</span>(<span class="pl-smi">first</span>, <span class="pl-smi">second</span>) {}
}

<span class="pl-k">class</span> <span class="pl-en">Child</span> <span class="pl-k">extends</span> <span class="pl-e">Parent</span> {
  @override
  <span class="pl-en">speak</span>() {}
  <span class="pl-c"><span class="pl-c">//</span> SyntaxError: Child#speak() does not properly override Parent#speak(first, second)</span>
}

<span class="pl-c"><span class="pl-c">//</span> or</span>

<span class="pl-k">class</span> <span class="pl-en">Child</span> <span class="pl-k">extends</span> <span class="pl-e">Parent</span> {
  @override
  <span class="pl-en">speaks</span>() {}
  <span class="pl-c"><span class="pl-c">//</span> SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.</span>
  <span class="pl-c"><span class="pl-c">//</span></span>
  <span class="pl-c"><span class="pl-c">//</span>   Did you mean "speak"?</span>
}</pre>
    </div>
    <p><strong>（4）@deprecate (别名@deprecated)</strong></p>
    <p><code>deprecate</code>或<code>deprecated</code>修饰器在控制台显示一条警告，表示该方法将废除。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">deprecate</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>core-decorators<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @deprecate
  <span class="pl-en">facepalm</span>() {}

  @<span class="pl-en">deprecate</span>(<span class="pl-s"><span class="pl-pds">'</span>We stopped facepalming<span
                class="pl-pds">'</span></span>)
  <span class="pl-en">facepalmHard</span>() {}

  @<span class="pl-en">deprecate</span>(<span class="pl-s"><span class="pl-pds">'</span>We stopped facepalming<span
                class="pl-pds">'</span></span>, { url<span class="pl-k">:</span> <span class="pl-s"><span
                class="pl-pds">'</span>http://knowyourmeme.com/memes/facepalm<span class="pl-pds">'</span></span> })
  <span class="pl-en">facepalmHarder</span>() {}
}

<span class="pl-k">let</span> person <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Person</span>();

<span class="pl-smi">person</span>.<span class="pl-en">facepalm</span>();
<span class="pl-c"><span class="pl-c">//</span> DEPRECATION Person#facepalm: This function will be removed in future versions.</span>

<span class="pl-smi">person</span>.<span class="pl-en">facepalmHard</span>();
<span class="pl-c"><span class="pl-c">//</span> DEPRECATION Person#facepalmHard: We stopped facepalming</span>

<span class="pl-smi">person</span>.<span class="pl-en">facepalmHarder</span>();
<span class="pl-c"><span class="pl-c">//</span> DEPRECATION Person#facepalmHarder: We stopped facepalming</span>
<span class="pl-c"><span class="pl-c">//</span></span>
<span class="pl-c"><span class="pl-c">//</span>     See http://knowyourmeme.com/memes/facepalm for more details.</span>
<span class="pl-c"><span class="pl-c">//</span></span></pre>
    </div>
    <p><strong>（5）@suppressWarnings</strong></p>
    <p><code>suppressWarnings</code>修饰器抑制<code>deprecated</code>修饰器导致的<code>console.warn()</code>调用。但是，异步代码发出的调用除外。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span class="pl-smi">suppressWarnings</span> } <span
            class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>core-decorators<span
            class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">Person</span> {
  @deprecated
  <span class="pl-en">facepalm</span>() {}

  @suppressWarnings
  <span class="pl-en">facepalmWithoutWarning</span>() {
    <span class="pl-c1">this</span>.<span class="pl-en">facepalm</span>();
  }
}

<span class="pl-k">let</span> person <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Person</span>();

<span class="pl-smi">person</span>.<span class="pl-en">facepalmWithoutWarning</span>();
<span class="pl-c"><span class="pl-c">//</span> no warning is logged</span></pre>
    </div>
    <h2><a href="#使用修饰器实现自动发布事件"  class="anchor" id="user-content-使用修饰器实现自动发布事件">

    </a>使用修饰器实现自动发布事件
    </h2>
    <p>我们可以使用修饰器，使得对象的方法被调用时，自动发出一个事件。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-smi">postal</span> <span
            class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">"</span>postal/lib/postal.lodash<span
            class="pl-pds">"</span></span>;

<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">function</span> <span
                class="pl-en">publish</span>(<span class="pl-smi">topic</span>, <span class="pl-smi">channel</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">name</span>, <span class="pl-smi">descriptor</span>) {
    <span class="pl-k">const</span> <span class="pl-c1">fn</span> <span class="pl-k">=</span> <span class="pl-smi">descriptor</span>.<span
                class="pl-c1">value</span>;

    <span class="pl-smi">descriptor</span>.<span class="pl-en">value</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span>() {
      <span class="pl-k">let</span> value <span class="pl-k">=</span> <span class="pl-smi">fn</span>.<span
                class="pl-c1">apply</span>(<span class="pl-c1">this</span>, <span class="pl-c1">arguments</span>);
      <span class="pl-smi">postal</span>.<span class="pl-en">channel</span>(channel <span class="pl-k">||</span> <span
                class="pl-smi">target</span>.<span class="pl-smi">channel</span> <span class="pl-k">||</span> <span
                class="pl-s"><span class="pl-pds">"</span>/<span class="pl-pds">"</span></span>).<span class="pl-en">publish</span>(topic, value);
    };
  };
}</pre>
    </div>
    <p>上面代码定义了一个名为<code>publish</code>的修饰器，它通过改写<code>descriptor.value</code>，使得原方法被调用时，会自动发出一个事件。它使用的事件“发布/订阅”库是<a
            href="https://github.com/postaljs/postal.js">Postal.js</a>。</p>
    <p>它的用法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-smi">publish</span> <span
            class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">"</span>path/to/decorators/publish<span
            class="pl-pds">"</span></span>;

<span class="pl-k">class</span> <span class="pl-en">FooComponent</span> {
  @<span class="pl-en">publish</span>(<span class="pl-s"><span class="pl-pds">"</span>foo.some.message<span
                class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>component<span
                class="pl-pds">"</span></span>)
  <span class="pl-en">someMethod</span>() {
    <span class="pl-k">return</span> {
      my<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>data<span
                class="pl-pds">"</span></span>
    };
  }
  @<span class="pl-en">publish</span>(<span class="pl-s"><span class="pl-pds">"</span>foo.some.other<span
                class="pl-pds">"</span></span>)
  <span class="pl-en">anotherMethod</span>() {
    <span class="pl-c"><span class="pl-c">//</span> ...</span>
  }
}</pre>
    </div>
    <p>以后，只要调用<code>someMethod</code>或者<code>anotherMethod</code>，就会自动发出一个事件。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> foo <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">FooComponent</span>();

<span class="pl-smi">foo</span>.<span class="pl-en">someMethod</span>() <span class="pl-c"><span class="pl-c">//</span> 在"component"频道发布"foo.some.message"事件，附带的数据是{ my: "data" }</span>
<span class="pl-smi">foo</span>.<span class="pl-en">anotherMethod</span>() <span class="pl-c"><span
                class="pl-c">//</span> 在"/"频道发布"foo.some.other"事件，不附带数据</span></pre>
    </div>
    <h2><a href="#mixin"  class="anchor" id="user-content-mixin">

    </a>Mixin
    </h2>
    <p>在修饰器的基础上，可以实现<code>Mixin</code>模式。所谓<code>Mixin</code>模式，就是对象继承的一种替代方案，中文译为“混入”（mix in），意为在一个对象之中混入另外一个对象的方法。</p>
    <p>请看下面的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">Foo</span> <span
            class="pl-k">=</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}

<span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-smi">MyClass</span>.<span
                class="pl-c1">prototype</span>, Foo);

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> 'foo'</span></pre>
    </div>
    <p>上面代码之中，对象<code>Foo</code>有一个<code>foo</code>方法，通过<code>Object.assign</code>方法，可以将<code>foo</code>方法“混入”<code>MyClass</code>类，导致<code>MyClass</code>的实例<code>obj</code>对象都具有<code>foo</code>方法。这就是“混入”模式的一个简单实现。
    </p>
    <p>下面，我们部署一个通用脚本<code>mixins.js</code>，将 Mixin 写成一个修饰器。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> <span class="pl-k">function</span> <span
            class="pl-en">mixins</span>(<span class="pl-k">...</span><span class="pl-v">list</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span> (<span class="pl-smi">target</span>) {
    <span class="pl-c1">Object</span>.<span class="pl-en">assign</span>(<span class="pl-smi">target</span>.<span
                class="pl-c1">prototype</span>, <span class="pl-k">...</span>list);
  };
}</pre>
    </div>
    <p>然后，就可以使用上面这个修饰器，为类“混入”各种方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">mixins</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>./mixins<span class="pl-pds">'</span></span>;

<span class="pl-k">const</span> <span class="pl-c1">Foo</span> <span class="pl-k">=</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">mixins</span>(Foo)
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> "foo"</span></pre>
    </div>
    <p>通过<code>mixins</code>这个修饰器，实现了在<code>MyClass</code>类上面“混入”<code>Foo</code>对象的<code>foo</code>方法。</p>
    <p>不过，上面的方法会改写<code>MyClass</code>类的<code>prototype</code>对象，如果不喜欢这一点，也可以通过类的继承实现 Mixin。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyClass</span> <span class="pl-k">extends</span> <span class="pl-e">MyBaseClass</span> {
  <span class="pl-c"><span class="pl-c">/*</span> ... <span class="pl-c">*/</span></span>
}</pre>
    </div>
    <p>上面代码中，<code>MyClass</code>继承了<code>MyBaseClass</code>。如果我们想在<code>MyClass</code>里面“混入”一个<code>foo</code>方法，一个办法是在<code>MyClass</code>和<code>MyBaseClass</code>之间插入一个混入类，这个类具有<code>foo</code>方法，并且继承了<code>MyBaseClass</code>的所有方法，然后<code>MyClass</code>再继承这个类。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> <span
            class="pl-en">MyMixin</span> <span class="pl-k">=</span> (<span class="pl-smi">superclass</span>) <span
            class="pl-k">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span
            class="pl-e">superclass</span> {
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>foo from MyMixin<span
                class="pl-pds">'</span></span>);
  }
};</pre>
    </div>
    <p>
        上面代码中，<code>MyMixin</code>是一个混入类生成器，接受<code>superclass</code>作为参数，然后返回一个继承<code>superclass</code>的子类，该子类包含一个<code>foo</code>方法。
    </p>
    <p>接着，目标类再去继承这个混入类，就达到了“混入”<code>foo</code>方法的目的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyClass</span> <span class="pl-k">extends</span> <span class="pl-e">MyMixin</span>(MyBaseClass) {
  <span class="pl-c"><span class="pl-c">/*</span> ... <span class="pl-c">*/</span></span>
}

<span class="pl-k">let</span> c <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">c</span>.<span class="pl-en">foo</span>(); <span class="pl-c"><span class="pl-c">//</span> "foo from MyMixin"</span></pre>
    </div>
    <p>如果需要“混入”多个方法，就生成多个混入类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyClass</span> <span class="pl-k">extends</span> <span class="pl-e">Mixin1</span>(<span
            class="pl-en">Mixin2</span>(MyBaseClass)) {
  <span class="pl-c"><span class="pl-c">/*</span> ... <span class="pl-c">*/</span></span>
}</pre>
    </div>
    <p>这种写法的一个好处，是可以调用<code>super</code>，因此可以避免在“混入”过程中覆盖父类的同名方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> <span
            class="pl-en">Mixin1</span> <span class="pl-k">=</span> (<span class="pl-smi">superclass</span>) <span
            class="pl-k">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span
            class="pl-e">superclass</span> {
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>foo from Mixin1<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">if</span> (<span class="pl-c1">super</span>.<span class="pl-smi">foo</span>) <span class="pl-c1">super</span>.<span
                class="pl-en">foo</span>();
  }
};

<span class="pl-k">let</span> <span class="pl-en">Mixin2</span> <span class="pl-k">=</span> (<span class="pl-smi">superclass</span>) <span
                class="pl-k">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span
                class="pl-e">superclass</span> {
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>foo from Mixin2<span
                class="pl-pds">'</span></span>);
    <span class="pl-k">if</span> (<span class="pl-c1">super</span>.<span class="pl-smi">foo</span>) <span class="pl-c1">super</span>.<span
                class="pl-en">foo</span>();
  }
};

<span class="pl-k">class</span> <span class="pl-en">S</span> {
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>foo from S<span
                class="pl-pds">'</span></span>);
  }
}

<span class="pl-k">class</span> <span class="pl-en">C</span> <span class="pl-k">extends</span> <span
                class="pl-e">Mixin1</span>(<span class="pl-en">Mixin2</span>(<span class="pl-c1">S</span>)) {
  <span class="pl-en">foo</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>foo from C<span
                class="pl-pds">'</span></span>);
    <span class="pl-c1">super</span>.<span class="pl-en">foo</span>();
  }
}</pre>
    </div>
    <p>上面代码中，每一次<code>混入</code>发生时，都调用了父类的<code>super.foo</code>方法，导致父类的同名方法没有被覆盖，行为被保留了下来。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">new</span> <span class="pl-en">C</span>().<span
            class="pl-en">foo</span>()
<span class="pl-c"><span class="pl-c">//</span> foo from C</span>
<span class="pl-c"><span class="pl-c">//</span> foo from Mixin1</span>
<span class="pl-c"><span class="pl-c">//</span> foo from Mixin2</span>
<span class="pl-c"><span class="pl-c">//</span> foo from S</span></pre>
    </div>
    <h2><a href="#trait"  class="anchor" id="user-content-trait">

    </a>Trait
    </h2>
    <p>Trait 也是一种修饰器，效果与 Mixin 类似，但是提供更多功能，比如防止同名方法的冲突、排除混入某些方法、为混入的方法起别名等等。</p>
    <p>下面采用<a
            href="https://github.com/CocktailJS/traits-decorator">traits-decorator</a>这个第三方模块作为例子。这个模块提供的<code>traits</code>修饰器，不仅可以接受对象，还可以接受
        ES6 类作为参数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">traits</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>traits-decorator<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">TFoo</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
}

<span class="pl-k">const</span> <span class="pl-c1">TBar</span> <span class="pl-k">=</span> {
  <span class="pl-en">bar</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">traits</span>(TFoo, TBar)
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> { }

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> foo</span>
<span class="pl-smi">obj</span>.<span class="pl-en">bar</span>() <span class="pl-c"><span
                class="pl-c">//</span> bar</span></pre>
    </div>
    <p>
        上面代码中，通过<code>traits</code>修饰器，在<code>MyClass</code>类上面“混入”了<code>TFoo</code>类的<code>foo</code>方法和<code>TBar</code>对象的<code>bar</code>方法。
    </p>
    <p>Trait 不允许“混入”同名方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">traits</span> } <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>traits-decorator<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">TFoo</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
}

<span class="pl-k">const</span> <span class="pl-c1">TBar</span> <span class="pl-k">=</span> {
  <span class="pl-en">bar</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>) },
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">traits</span>(TFoo, TBar)
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> { }
<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-c"><span
        class="pl-c">//</span> throw new Error('Method named: ' + methodName + ' is defined twice.');</span>
<span class="pl-c"><span class="pl-c">//</span>        ^</span>
<span class="pl-c"><span class="pl-c">//</span> Error: Method named: foo is defined twice.</span></pre>
    </div>
    <p>上面代码中，<code>TFoo</code>和<code>TBar</code>都有<code>foo</code>方法，结果<code>traits</code>修饰器报错。</p>
    <p>一种解决方法是排除<code>TBar</code>的<code>foo</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">traits</span>, <span class="pl-smi">excludes</span> } <span class="pl-k">from</span> <span
            class="pl-s"><span class="pl-pds">'</span>traits-decorator<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">TFoo</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
}

<span class="pl-k">const</span> <span class="pl-c1">TBar</span> <span class="pl-k">=</span> {
  <span class="pl-en">bar</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>) },
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">traits</span>(TFoo, TBar<span class="pl-k">::</span><span class="pl-en">excludes</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>))
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> { }

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> foo</span>
<span class="pl-smi">obj</span>.<span class="pl-en">bar</span>() <span class="pl-c"><span
                class="pl-c">//</span> bar</span></pre>
    </div>
    <p>上面代码使用绑定运算符（::）在<code>TBar</code>上排除<code>foo</code>方法，混入时就不会报错了。</p>
    <p>另一种方法是为<code>TBar</code>的<code>foo</code>方法起一个别名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span
            class="pl-smi">traits</span>, <span class="pl-smi">alias</span> } <span class="pl-k">from</span> <span
            class="pl-s"><span class="pl-pds">'</span>traits-decorator<span class="pl-pds">'</span></span>;

<span class="pl-k">class</span> <span class="pl-en">TFoo</span> {
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
}

<span class="pl-k">const</span> <span class="pl-c1">TBar</span> <span class="pl-k">=</span> {
  <span class="pl-en">bar</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>) },
  <span class="pl-en">foo</span>() { <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>) }
};

@<span class="pl-en">traits</span>(TFoo, TBar<span class="pl-k">::</span><span class="pl-en">alias</span>({foo<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>aliasFoo<span
                class="pl-pds">'</span></span>}))
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> { }

<span class="pl-k">let</span> obj <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();
<span class="pl-smi">obj</span>.<span class="pl-en">foo</span>() <span class="pl-c"><span
                class="pl-c">//</span> foo</span>
<span class="pl-smi">obj</span>.<span class="pl-en">aliasFoo</span>() <span class="pl-c"><span class="pl-c">//</span> foo</span>
<span class="pl-smi">obj</span>.<span class="pl-en">bar</span>() <span class="pl-c"><span
                class="pl-c">//</span> bar</span></pre>
    </div>
    <p>
        上面代码为<code>TBar</code>的<code>foo</code>方法起了别名<code>aliasFoo</code>，于是<code>MyClass</code>也可以混入<code>TBar</code>的<code>foo</code>方法了。
    </p>
    <p><code>alias</code>和<code>excludes</code>方法，可以结合起来使用。</p>
    <div class="highlight highlight-source-js"><pre>@<span class="pl-en">traits</span>(TExample<span
            class="pl-k">::</span><span class="pl-en">excludes</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
            class="pl-pds">'</span></span>,<span class="pl-s"><span class="pl-pds">'</span>bar<span
            class="pl-pds">'</span></span>)<span class="pl-k">::</span><span class="pl-en">alias</span>({baz<span
            class="pl-k">:</span><span class="pl-s"><span class="pl-pds">'</span>exampleBaz<span class="pl-pds">'</span></span>}))
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}</pre>
    </div>
    <p>上面代码排除<code>了TExample</code>的<code>foo</code>方法和<code>bar</code>方法，为<code>baz</code>方法起了别名<code>exampleBaz</code>。
    </p>
    <p><code>as</code>方法则为上面的代码提供了另一种写法。</p>
    <div class="highlight highlight-source-js"><pre>@<span class="pl-en">traits</span>(TExample<span
            class="pl-k">::</span><span class="pl-en">as</span>({excludes<span class="pl-k">:</span>[<span class="pl-s"><span
            class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>, <span class="pl-s"><span
            class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>], alias<span class="pl-k">:</span> {baz<span
            class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>exampleBaz<span
            class="pl-pds">'</span></span>}}))
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {}</pre>
    </div>
    <h2><a href="#babel-转码器的支持"  class="anchor" id="user-content-babel-转码器的支持">

    </a>Babel 转码器的支持
    </h2>
    <p>目前，Babel 转码器已经支持 Decorator。</p>
    <p>
        首先，安装<code>babel-core</code>和<code>babel-plugin-transform-decorators</code>。由于后者包括在<code>babel-preset-stage-0</code>之中，所以改为安装<code>babel-preset-stage-0</code>亦可。
    </p>
    <div class="highlight highlight-source-shell">
        <pre>$ npm install babel-core babel-plugin-transform-decorators</pre>
    </div>
    <p>然后，设置配置文件<code>.babelrc</code>。</p>
    <div class="highlight highlight-source-js"><pre>{
  <span class="pl-s"><span class="pl-pds">"</span>plugins<span class="pl-pds">"</span></span><span class="pl-k">:</span> [<span
                class="pl-s"><span class="pl-pds">"</span>transform-decorators<span class="pl-pds">"</span></span>]
}</pre>
    </div>
    <p>这时，Babel 就可以对 Decorator 转码了。</p>
    <p>脚本中打开的命令如下。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-smi">babel</span>.<span class="pl-en">transform</span>(<span class="pl-s"><span
                class="pl-pds">"</span>code<span class="pl-pds">"</span></span>, {plugins<span
                class="pl-k">:</span> [<span class="pl-s"><span class="pl-pds">"</span>transform-decorators<span
                class="pl-pds">"</span></span>]})</pre>
    </div>
    <p>Babel 的官方网站提供一个<a href="https://babeljs.io/repl/" rel="nofollow">在线转码器</a>，只要勾选 Experimental，就能支持 Decorator
        的在线转码。</p>
</div>
</body>
</html>
